import {
  useAsyncEffect,
  useDebounceFn,
  useGetState,
  useMemoizedFn,
  useUnmount,
  useUnmountedRef,
} from 'ahooks';
import { Spin } from 'antd';
import { useRef } from 'react';
import { formatDate } from 'web-plugin-utils';
import styles from './index.less';

export const WHEPPlayer = ({
  url,
  play,
  controls,
  muted,
  cache,
}: {
  /**
   * 流媒体路径
   */
  url: string;
  /**
   * 是否播放
   */
  play: boolean;
  /**
   * 控制栏
   */
  controls?: boolean;
  /**
   * 是否静音
   */
  muted?: boolean;
  cache?: boolean;
}) => {
  muted = muted == undefined ? true : muted;
  cache = cache == undefined ? false : cache;
  controls = controls == undefined ? false : controls;
  const videoRef = useRef<HTMLVideoElement>(null);
  const [playIng, setPlayIng, getPlayIng] = useGetState(false);
  const [, setConnectIng, getConnectIng] = useGetState(false);
  const [, setPeer, getPeer] = useGetState<RTCPeerConnection>();
  const [, setVideoMediaStream, getVideoMediaStream] =
    useGetState<MediaStream>();
  const unmountRef: { current: boolean } = useUnmountedRef();

  const log = (log: string) => {
    console.log(
      `[${formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')}][WebRTC] ${log}`,
    );
  };

  const destroyVideo = useMemoizedFn((force: boolean = false) => {
    const closePeer = () => {
      log(`销毁WebRTC连接`);
      // @ts-ignore
      if (cache && window[url]) {
        log(`销毁缓存连接`);
        // @ts-ignore
        const { peerConnection, mediaStream } = window[url] as {
          peerConnection: RTCPeerConnection;
          mediaStream: MediaStream;
        };
        mediaStream?.getTracks().forEach((t) => t.stop());
        peerConnection?.close();
        // @ts-ignore
        window[url] = undefined;
      }

      setConnectIng(false);
      setPlayIng(false);
      videoRef.current?.pause();
      getVideoMediaStream()
        ?.getTracks()
        .forEach((t) => t.stop());
      getPeer()?.close();
    };
    if (cache) {
      const connectionState = getPeer()?.connectionState;
      if (
        force ||
        (connectionState != undefined && connectionState != 'connected')
      ) {
        closePeer();
      }
    } else {
      closePeer();
    }
  });

  const { run: replay } = useDebounceFn(
    useMemoizedFn((logMessage?: string) => {
      if (!unmountRef.current) {
        if (logMessage) {
          log(logMessage);
        }
        destroyVideo(true);
        setTimeout(playVideo, 1000);
      }
    }),
    { wait: 5000 },
  );

  const { run: replayBySdp } = useDebounceFn(
    useMemoizedFn(() => {
      if (!getPlayIng() && !unmountRef.current) {
        log('SDP交换成功后5秒没有正常播放，重置播放器');
        destroyVideo(true);
        setTimeout(playVideo, 1000);
      }
    }),
    { wait: 5000 },
  );

  const videoPlay = (
    videoElement: HTMLVideoElement,
    videoStream: MediaStream,
    cachePlay: boolean = false,
  ) => {
    videoElement.srcObject = videoStream;
    videoElement
      .play()
      .then(() => {
        setPlayIng(true);
        log(`播放成功`);
      })
      .catch(() => {
        setPlayIng(false);
        log(`播放失败`);
        replay(`视频播放失败，重置播放器`);
      });
    if (cachePlay) {
      replayBySdp();
    }
    videoElement.onclick = (ev) => {
      ev.preventDefault();
    };
    videoElement.onplaying = () => {
      // log('onplaying');
      setPlayIng(true);
      setConnectIng(false);
    };
    videoElement.ontimeupdate = () => {
      // log('ontimeupdate', e);
      replay(`视频流卡住，重置播放器`);
    };
  };

  const peerEvent = useMemoizedFn(
    (peer: RTCPeerConnection, videoStream: MediaStream) => {
      if (peer.getTransceivers().length <= 0) {
        peer.addTransceiver('video', { direction: 'recvonly' });
        peer.addTransceiver('audio', { direction: 'recvonly' });
      }
      peer.onconnectionstatechange = () => {
        log(`SDP Connect Status: ${peer.connectionState}`);
        if (peer.connectionState === 'connected') {
          replayBySdp();
        } else if (
          peer.connectionState === 'disconnected' ||
          peer.connectionState === 'failed'
        ) {
          replay('WebRTC断开，重置播放器');
        }
      };
      peer.ontrack = (e) => {
        videoStream.addTrack(e.track);
      };
    },
  );

  const playForWebRTC = useMemoizedFn(async () => {
    log(`Play WHEP URL: ${url}`);
    const videoStream = new MediaStream();
    const peer = new RTCPeerConnection();
    peerEvent(peer, videoStream);
    setVideoMediaStream(videoStream);
    setPeer(peer);
    if (cache) {
      // @ts-ignore
      window[url] = {
        peerConnection: peer,
        mediaStream: videoStream,
      };
    }

    try {
      const rtcSdp = await peer.createOffer();
      await peer.setLocalDescription(rtcSdp);
      const result = await fetch(url, {
        method: 'POST',
        // mode: 'cors',
        cache: 'no-cache',
        // credentials: 'include',
        redirect: 'follow',
        referrerPolicy: 'no-referrer',
        headers: {
          'Content-Type': 'application/sdp',
        },
        body: rtcSdp.sdp,
      });
      const remoteSdp = await result.text();
      await peer.setRemoteDescription(
        new RTCSessionDescription({ type: 'answer', sdp: remoteSdp }),
      );
    } catch (e: any) {
      log(`SDP交换失败` + e.message);
      destroyVideo(true);
      setTimeout(playVideo, 1000);
    }

    return {
      peerConnection: peer,
      mediaStream: videoStream,
    };
  });

  const playVideo = useMemoizedFn(async () => {
    if (!play) {
      setConnectIng(false);
      // const { peerConnection } = window[url] as {
      //   peerConnection: RTCPeerConnection;
      // };
      // peerConnection?.close();
      // destroyVideo();
      return;
    }
    if (getConnectIng()) {
      return;
    }
    if (getPlayIng()) {
      destroyVideo();
    }

    const videoElement = videoRef.current;
    if (!videoElement) {
      setTimeout(playVideo, 1000);
      return;
    }
    let videoStream: MediaStream;
    let peer: RTCPeerConnection;

    setPlayIng(false);
    setConnectIng(true);

    // @ts-ignore
    if (cache && window[url]) {
      // @ts-ignore
      const { peerConnection, mediaStream } = window[url] as {
        peerConnection: RTCPeerConnection;
        mediaStream: MediaStream;
      };

      if (peerConnection.connectionState === 'connected') {
        videoStream = mediaStream;
        peer = peerConnection;
        peerEvent(peer, videoStream);
        setVideoMediaStream(videoStream);
        setPeer(peer);
        log(`Cache Play MediaStream ${videoStream.active}`);
        videoPlay(videoElement, videoStream, true);
      } else {
        const { mediaStream } = await playForWebRTC();
        videoStream = mediaStream;
        videoPlay(videoElement, videoStream);
      }
    } else {
      const { mediaStream } = await playForWebRTC();
      videoStream = mediaStream;
      videoPlay(videoElement, videoStream);
    }

    return destroyVideo;
  });

  useAsyncEffect(playVideo as any, [play]);
  useAsyncEffect(playVideo as any, [url]);
  useUnmount(destroyVideo);

  return (
    <>
      {!playIng && (
        <Spin
          wrapperClassName={styles.wrapperSpin}
          spinning={true}
          tip="视频加载中..."
        >
          <div></div>
        </Spin>
      )}
      <video
        className={styles.whepPlayer}
        style={{
          width: '100%',
          height: '100%',
          display: playIng ? 'block' : 'none',
        }}
        ref={videoRef}
        controls={controls}
        muted={muted}
        disablePictureInPicture={true}
        disableRemotePlayback={true}
        controlsList="nodownload noremoteplayback"
      />
    </>
  );
};
